import random, math

from maya import cmds
from maya import OpenMaya as om
from AntsCrowd import Define , rayIntersect


reload( Define )
reload( rayIntersect )

class Terrain:
    instance = None

    def __init__( self ):
        Terrain.instance = self
        self.mPlane = None
        self.mPixellist = None
        self.mNormallist = None
        self.mWidth = 0
        self.mHeight = 0
        self.min = None
        self.max = None
        self.mTerrainHeight = 0
        self.mTerrainWidth = 0
        self.mScaleFactor = 0
        self.mCamera = None
        self.mArrangeCube = False        
        
    def createTerrain(self, _Filename, _ScaleFactor):
        self.cam = cmds.ls('antCrowd_cam_*')[0] if cmds.ls('antCrowd_cam_*') != [] and \
                    cmds.objExists(cmds.ls('antCrowd_cam_*')[0])  else 'persp'        
        print 'create plane'
        
        if(_Filename == None):
            self.mPlane =  'bg_grd_geo'          
            bound=cmds.xform( self.mPlane , bb = 1 , q = 1 )                    
            self.min = [bound[0],bound[1],bound[2]]
            self.max = [bound[3],bound[4],bound[5]]            
            self.mTerrainWidth = self.max[0] - self.min[0]
            self.mTerrainHeight = self.max[2] - self.min[2]
            self.mCamera = cmds.ls('antCrowd_cam_*')            
            #Define.loddist[0] = self.mTerrainWidth/3
            #Define.loddist[1] = (self.mTerrainWidth/3 )*2  
            #Define.loddist[2]= (self.mTerrainWidth/3 )*3 
        else:
            self.mPlane = cmds.polyPlane()
            #Load Image
            _image = om.MImage()
            _image.readFromFile( _Filename )  
            _script = om.MScriptUtil()
            _widthptr = _script.asUintPtr()
            _heightptr = _script.asUintPtr()
            _script.setUint( _widthptr, 0 )
            _script.setUint( _heightptr, 0 )
            _image.getSize( _widthptr, _heightptr )
            self.mWidth = _script.getUint(_widthptr)
            self.mHeight = _script.getUint(_heightptr)
            
            #Image pixel data list
            self.mPixellist = [[0 for _xIdx in xrange(self.mWidth)]for _yIdx in xrange(self.mHeight)] 
            self.mNormallist = [[0 for _xIdx in xrange(self.mWidth-1)]for _yIdx in xrange(self.mHeight-1)]      
            _charPixelPtr = _image.pixels()
            for _yIdx in range(0,self.mHeight):
                for _xIdx in range(0,self.mWidth):    
                    _pixel = _script.getUcharArrayItem(_charPixelPtr,_yIdx*self.mWidth*4+_xIdx*4)                    
                    self.mPixellist[_xIdx][_yIdx] = _pixel * (_ScaleFactor * 0.06 )
            self.mScaleFactor = _ScaleFactor            
            self.mTerrainWidth = self.mWidth * self.mScaleFactor
            self.mTerrainHeight = self.mHeight * self.mScaleFactor
            cmds.setAttr( self.mPlane[1] + '.width', self.mTerrainWidth )
            cmds.setAttr( self.mPlane[1] + '.height', self.mTerrainHeight )                 
            
            cmds.setAttr( self.mPlane[1] + '.subdivisionsWidth', self.mWidth - 1 )
            cmds.setAttr( self.mPlane[1] + '.subdivisionsHeight', self.mHeight - 1 )  
            for _yIdx in range(self.mHeight):
                for _xIdx in range(self.mWidth):
                    pixel = self.mPixellist[_xIdx][_yIdx]                    
                    cmds.setAttr("pPlaneShape1.pnts["+str(self.mWidth*_yIdx+_xIdx)+"].pnty", pixel )
            for _yIdx in range(0,self.mHeight - 1):
                for _xIdx in range(0,self.mWidth - 1):
                    vec = [ float(x) for x in cmds.polyInfo( self.mPlane[0]+'.f['+ str((self.mWidth-1)*_yIdx+_xIdx) +']',fn=1)[0].split(':')[1].split('\n')[0].split(' ')[1:] ]
                    self.mNormallist[_xIdx][_yIdx] =om.MVector(vec[0],vec[1],vec[2])
                    
                    

    #Heightmap based function    
    def fnGetInfo(self,posx,posz,_head):
        '''
        Return List
        [0] RotateX
        [1] RotateY
        [2] RotateZ
        [3] Height
        '''
                  
        posx += self.mTerrainWidth/2              
        posz =  self.mTerrainHeight/2-posz
               
        imagex = posx*self.mWidth/self.mTerrainWidth        
        imagey = posz*self.mHeight/self.mTerrainHeight           

        if(0<=imagex<self.mWidth and 0<=imagey<self.mHeight ):                    
            pixel = self.mPixellist[int(imagex)][int(imagey)]
            tgheight = pixel
        else:
            tgheight = -1000


        normalx = posx*(self.mWidth-1)/self.mTerrainWidth        
        normaly = posz*(self.mHeight-1)/self.mTerrainHeight           

        #normalx = (self.mWidth-1) - normalx
        #normaly = (self.mHeight-1) - normaly

        if(0<=normalx<(self.mWidth-1) and 0<=normaly<(self.mHeight-1) ):                    
            _vNormal = self.mNormallist[int(normalx)][int(normaly)]  
            
        else:            
            _vNormal = om.MVector(0,1,0)            
                     
        _vNormal.normalize()               
        _vHead = om.MVector(_head.x,0,_head.y)
        _vHead.normalize()
        
        _vDir = _vHead ^ _vNormal
        _vRight= _vDir ^ _vNormal                        
        
        _matlist = [_vRight.x,_vNormal.x,_vDir.x,0,\
                    _vRight.y,_vNormal.y,_vDir.y,0,\
                    _vRight.z,_vNormal.z,_vDir.z,0,\
                    0,0,0,1] 

        _rotateM = FCMatrix.getMMatrix(_matlist)             
        #_rotateM.transpose()
        _matlist = FCMatrix.setMMatrix(_rotateM)  

        if _matlist[4]>0.998:
            _heading = math.atan2(_matlist[2],_matlist[10])
            _attitude = transform.PI/2
            _bank = 0            
            return _attitude,_heading,_bank,tgheight

        if _matlist[4]<-0.998:
            _heading = math.atan2(_matlist[3],_matlist[10])
            _attitude = transform.PI/2
            _bank = 0            
            return _attitude,_heading,_bank,tgheight

        #Y rotate
        _heading = math.atan2(-_matlist[8],_matlist[0])
        #z rotate
        _bank = math.atan2(-_matlist[6],_matlist[5])
        #x rotate
        _attitude = math.asin(_matlist[4]) 

        return _attitude,_heading,_bank,tgheight        
        
    #Rayintersection based function         
    def fnGetXZRotateAndHeight(self,_posx,_posy,_posz,_head):
        '''
        Return List
        [0] RotateX
        [1] RotateY
        [2] RotateZ
        [3] Height
        '''  
        _point = [ _posx, _posy, _posz ]
        _result = rayIntersect.ray( self.mPlane, _point )
        _height = _result[0]
        _normal = _result[1]
        _front = om.MVector( _head.x, 0, _head.y )
        _front = _front.normal()
        
        _direction = _front ^ _normal
        _side = _direction ^_normal
        
        _matlist = [_side.x, _normal.x, _direction.x, 0,\
                    _side.y, _normal.y, _direction.y, 0,\
                    _side.z, _normal.z, _direction.z, 0,\
                    0, 0, 0, 1]
                    
        _rotateM = FCMatrix.getMMatrix(_matlist)
        _matlist = FCMatrix.setMMatrix(_rotateM)
        
        if _matlist[4]>0.998:
            _heading = math.atan2(_matlist[2],_matlist[10])
            _attitude = math.pi/2
            _bank = 0
            return _attitude,_heading,_bank,_height
        if _matlist[4]<-0.998:
            _heading = math.atan2(_matlist[3],_matlist[10])
            _attitude = math.pi/2
            _bank = 0
            return _attitude,_heading,_bank,_height

        #Y rotate
        _heading = math.atan2(-_matlist[8],_matlist[0])
        #z rotate
        _bank = math.atan2(-_matlist[6],_matlist[5])
        #x rotate
        _attitude = math.asin(_matlist[4])
        
        return _attitude,_heading,_bank,_height
        
    def getRandPos( self ):
        return FCVector.vector2D( random.uniform( -self.mTerrainWidth / 2, self.mTerrainWidth / 2 ),\
                                  random.uniform( -self.mTerrainHeight / 2, self.mTerrainHeight / 2 ) )
                                  
    def getRandPosQuallity(self,_quality):        
        
        _randomXpos = ( random.uniform( -(self.mTerrainWidth / 2 )+ (self.mTerrainWidth/6) , (self.mTerrainWidth / 2) - (self.mTerrainWidth/6) ) )
        if (_quality == 'high'):
            #return FCVector.vector2D( _randomXpos , random.uniform(0  , -Define.loddist[0] ) )
            return self.getCamArrange(self.mCamera)
        elif (_quality == 'middle'):
            return FCVector.vector2D( _randomXpos , random.uniform( -Define.loddist[0]  , -Define.loddist[1] ) )
        else :
            return FCVector.vector2D( _randomXpos , random.uniform( -Define.loddist[1] , -Define.loddist[2] ) )
            
    def getRandPosQuallityCube(self,_quality):         
        if (_quality == 'high'):
            width =  (Define.arrangecube[2][1].x - Define.arrangecube[2][0].x) /2 
            height = (Define.arrangecube[2][1].y - Define.arrangecube[2][0].y) /2 
            xPos = random.uniform(Define.arrangecube[2][2].x-width,Define.arrangecube[2][2].x+width)
            yPos = random.uniform(Define.arrangecube[2][2].y-height,Define.arrangecube[2][2].y+height)
            return FCVector.vector2D( xPos , yPos)           
        elif (_quality == 'middle'):
            width =  (Define.arrangecube[1][1].x - Define.arrangecube[1][0].x) /2 
            height = (Define.arrangecube[1][1].y - Define.arrangecube[1][0].y) /2 
            xPos = random.uniform(Define.arrangecube[1][2].x-width,Define.arrangecube[1][2].x+width)
            yPos = random.uniform(Define.arrangecube[1][2].y-height,Define.arrangecube[1][2].y+height)
            return FCVector.vector2D( xPos , yPos)  
        else :
            width =  (Define.arrangecube[0][1].x - Define.arrangecube[0][0].x) /2             
            height = (Define.arrangecube[0][1].y - Define.arrangecube[0][0].y) /2 
            xPos = random.uniform(Define.arrangecube[0][2].x-width,Define.arrangecube[0][2].x+width)
            yPos = random.uniform(Define.arrangecube[0][2].y-height,Define.arrangecube[0][2].y+height)
            
            return FCVector.vector2D( xPos , yPos)  
        
        
    def getPiePos( self, _isHigh = False ):
        radius = self.mTerrainWidth / 2
        _height = 0
        if _isHigh:
            _height = self.mTerrainHeight / 8
        _vector = FCVector.vector2D( 0, 0 )
        _vector.x = radius * math.cos( math.radians( random.uniform(210, 330) ) )
        _vector.y = radius * math.sin( math.radians( random.uniform(210, 330) ) ) + _height
        return _vector
        
    def getGoalPos( self ):
        _goal = FCVector.vector2D( 0, 0 )
        _goal.x = 0
        _goal.y = self.mTerrainHeight / 2
        
        return _goal
        
    def getRangePos( self, _to, _from ):
        _value = random.uniform( _to, _from )
        _posX = (self.mTerrainWidth * 2 * _value) - self.mTerrainWidth
        _posX = (self.mTerrainWidth * 2 * _value) - self.mTerrainWidth
        
        return FCVector.vector2D( _posX, _posY )
        
    def getCamArrange(self,cam):
        _fov = cmds.getAttr(cam[2] + ".focalLength")
        _fov /= 2 
        _campos = cmds.xform( cam, ws=1, t=1, q=1 )                
        
        _height = math.fabs(Define.loddist[0] - _campos[2])
        _base = math.tan(_fov)*_height
        
        _dot1 = FCVector.vector2D(_campos[0],_campos[2])
        _dot2 = FCVector.vector2D(_campos[0] - _base ,_campos[2] - _height)
        _dot3 = FCVector.vector2D(_campos[0] + _base ,_campos[2] - _height)              
        

        deltaleft = (_dot2.x-_dot1.x)/(_dot2.y-_dot1.y)
        deltaright = (_dot3.x-_dot1.x)/(_dot3.y-_dot1.y)
        
        _posY = random.uniform(-Define.loddist[0],_campos[2])
        deltaY = math.fabs(_campos[2]-_posY)
        _posX = random.uniform(_campos[0]-(deltaY*deltaleft),_campos[0]+(deltaY*deltaleft))
        
        return FCVector.vector2D(_posX,_posY)
        
                
        